﻿using System;
using System.Collections.Generic;
using System.Text;
using DotNetCommon.Extensions;

namespace DotNetCommon
{
    /// <summary>
    /// 用于生成哈希码的Helper类。
    /// </summary>
    /// <remarks>可以将多个对象或一个数组内的全部对象做为一个整体(顺序也影响hashcode计算值)计算hashcode</remarks>
    public static class HashHelper
    {
        /// <summary>
        /// 一个相对较大的质数。
        /// </summary>
        private const int PrimeNumber = 486187739;

        /// <summary>
        /// 31 是移位和减法，因此非常快。
        /// </summary>
        private const int DefaultHashValue = 31;

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <param name="param">要进行计算哈希值的对象</param>
        public static int GetHashCode<T>(T param) => param.IsDefault() ? 0 : param.GetHashCode();

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参照: <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2>(T1 param1, T2 param2)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                return hash * PrimeNumber + GetHashCode(param2);
            }
        }

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="@ http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        /// <param name="param3">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2, T3>(T1 param1, T2 param2, T3 param3)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                hash = hash * PrimeNumber + GetHashCode(param2);
                return hash * PrimeNumber + GetHashCode(param3);
            }
        }

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        /// <param name="param3">要进行计算哈希值的对象</param>
        /// <param name="param4">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2, T3, T4>(T1 param1, T2 param2, T3 param3, T4 param4)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                hash = hash * PrimeNumber + GetHashCode(param2);
                hash = hash * PrimeNumber + GetHashCode(param3);
                return hash * PrimeNumber + GetHashCode(param4);
            }
        }

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        /// <param name="param3">要进行计算哈希值的对象</param>
        /// <param name="param4">要进行计算哈希值的对象</param>
        /// <param name="param5">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2, T3, T4, T5>(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                hash = hash * PrimeNumber + GetHashCode(param2);
                hash = hash * PrimeNumber + GetHashCode(param3);
                hash = hash * PrimeNumber + GetHashCode(param4);
                return hash * PrimeNumber + GetHashCode(param5);
            }
        }

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        /// <param name="param3">要进行计算哈希值的对象</param>
        /// <param name="param4">要进行计算哈希值的对象</param>
        /// <param name="param5">要进行计算哈希值的对象</param>
        /// <param name="param6">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2, T3, T4, T5, T6>(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                hash = hash * PrimeNumber + GetHashCode(param2);
                hash = hash * PrimeNumber + GetHashCode(param3);
                hash = hash * PrimeNumber + GetHashCode(param4);
                hash = hash * PrimeNumber + GetHashCode(param5);
                return hash * PrimeNumber + GetHashCode(param6);
            }
        }

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        /// <param name="param3">要进行计算哈希值的对象</param>
        /// <param name="param4">要进行计算哈希值的对象</param>
        /// <param name="param5">要进行计算哈希值的对象</param>
        /// <param name="param6">要进行计算哈希值的对象</param>
        /// <param name="param7">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7>(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                hash = hash * PrimeNumber + GetHashCode(param2);
                hash = hash * PrimeNumber + GetHashCode(param3);
                hash = hash * PrimeNumber + GetHashCode(param4);
                hash = hash * PrimeNumber + GetHashCode(param5);
                hash = hash * PrimeNumber + GetHashCode(param6);
                return hash * PrimeNumber + GetHashCode(param7);
            }
        }

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        /// <param name="param3">要进行计算哈希值的对象</param>
        /// <param name="param4">要进行计算哈希值的对象</param>
        /// <param name="param5">要进行计算哈希值的对象</param>
        /// <param name="param6">要进行计算哈希值的对象</param>
        /// <param name="param7">要进行计算哈希值的对象</param>
        /// <param name="param8">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8>(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                hash = hash * PrimeNumber + GetHashCode(param2);
                hash = hash * PrimeNumber + GetHashCode(param3);
                hash = hash * PrimeNumber + GetHashCode(param4);
                hash = hash * PrimeNumber + GetHashCode(param5);
                hash = hash * PrimeNumber + GetHashCode(param6);
                hash = hash * PrimeNumber + GetHashCode(param7);
                return hash * PrimeNumber + GetHashCode(param8);
            }
        }

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        /// <param name="param3">要进行计算哈希值的对象</param>
        /// <param name="param4">要进行计算哈希值的对象</param>
        /// <param name="param5">要进行计算哈希值的对象</param>
        /// <param name="param6">要进行计算哈希值的对象</param>
        /// <param name="param7">要进行计算哈希值的对象</param>
        /// <param name="param8">要进行计算哈希值的对象</param>
        /// <param name="param9">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8, T9>(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8, T9 param9)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                hash = hash * PrimeNumber + GetHashCode(param2);
                hash = hash * PrimeNumber + GetHashCode(param3);
                hash = hash * PrimeNumber + GetHashCode(param4);
                hash = hash * PrimeNumber + GetHashCode(param5);
                hash = hash * PrimeNumber + GetHashCode(param6);
                hash = hash * PrimeNumber + GetHashCode(param7);
                hash = hash * PrimeNumber + GetHashCode(param8);
                return hash * PrimeNumber + GetHashCode(param9);
            }
        }

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        /// <param name="param3">要进行计算哈希值的对象</param>
        /// <param name="param4">要进行计算哈希值的对象</param>
        /// <param name="param5">要进行计算哈希值的对象</param>
        /// <param name="param6">要进行计算哈希值的对象</param>
        /// <param name="param7">要进行计算哈希值的对象</param>
        /// <param name="param8">要进行计算哈希值的对象</param>
        /// <param name="param9">要进行计算哈希值的对象</param>
        /// <param name="param10">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8, T9 param9, T10 param10)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                hash = hash * PrimeNumber + GetHashCode(param2);
                hash = hash * PrimeNumber + GetHashCode(param3);
                hash = hash * PrimeNumber + GetHashCode(param4);
                hash = hash * PrimeNumber + GetHashCode(param5);
                hash = hash * PrimeNumber + GetHashCode(param6);
                hash = hash * PrimeNumber + GetHashCode(param7);
                hash = hash * PrimeNumber + GetHashCode(param8);
                hash = hash * PrimeNumber + GetHashCode(param9);
                return hash * PrimeNumber + GetHashCode(param10);
            }
        }

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        /// <param name="param3">要进行计算哈希值的对象</param>
        /// <param name="param4">要进行计算哈希值的对象</param>
        /// <param name="param5">要进行计算哈希值的对象</param>
        /// <param name="param6">要进行计算哈希值的对象</param>
        /// <param name="param7">要进行计算哈希值的对象</param>
        /// <param name="param8">要进行计算哈希值的对象</param>
        /// <param name="param9">要进行计算哈希值的对象</param>
        /// <param name="param10">要进行计算哈希值的对象</param>
        /// <param name="param11">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8, T9 param9, T10 param10, T11 param11)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                hash = hash * PrimeNumber + GetHashCode(param2);
                hash = hash * PrimeNumber + GetHashCode(param3);
                hash = hash * PrimeNumber + GetHashCode(param4);
                hash = hash * PrimeNumber + GetHashCode(param5);
                hash = hash * PrimeNumber + GetHashCode(param6);
                hash = hash * PrimeNumber + GetHashCode(param7);
                hash = hash * PrimeNumber + GetHashCode(param8);
                hash = hash * PrimeNumber + GetHashCode(param9);
                hash = hash * PrimeNumber + GetHashCode(param10);
                return hash * PrimeNumber + GetHashCode(param11);
            }
        }

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        /// <param name="param3">要进行计算哈希值的对象</param>
        /// <param name="param4">要进行计算哈希值的对象</param>
        /// <param name="param5">要进行计算哈希值的对象</param>
        /// <param name="param6">要进行计算哈希值的对象</param>
        /// <param name="param7">要进行计算哈希值的对象</param>
        /// <param name="param8">要进行计算哈希值的对象</param>
        /// <param name="param9">要进行计算哈希值的对象</param>
        /// <param name="param10">要进行计算哈希值的对象</param>
        /// <param name="param11">要进行计算哈希值的对象</param>
        /// <param name="param12">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8, T9 param9, T10 param10, T11 param11, T12 param12)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                hash = hash * PrimeNumber + GetHashCode(param2);
                hash = hash * PrimeNumber + GetHashCode(param3);
                hash = hash * PrimeNumber + GetHashCode(param4);
                hash = hash * PrimeNumber + GetHashCode(param5);
                hash = hash * PrimeNumber + GetHashCode(param6);
                hash = hash * PrimeNumber + GetHashCode(param7);
                hash = hash * PrimeNumber + GetHashCode(param8);
                hash = hash * PrimeNumber + GetHashCode(param9);
                hash = hash * PrimeNumber + GetHashCode(param10);
                hash = hash * PrimeNumber + GetHashCode(param11);
                return hash * PrimeNumber + GetHashCode(param12);
            }
        }

        /// <summary>
        /// 使用给定的参数获取哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="param1">要进行计算哈希值的对象</param>
        /// <param name="param2">要进行计算哈希值的对象</param>
        /// <param name="param3">要进行计算哈希值的对象</param>
        /// <param name="param4">要进行计算哈希值的对象</param>
        /// <param name="param5">要进行计算哈希值的对象</param>
        /// <param name="param6">要进行计算哈希值的对象</param>
        /// <param name="param7">要进行计算哈希值的对象</param>
        /// <param name="param8">要进行计算哈希值的对象</param>
        /// <param name="param9">要进行计算哈希值的对象</param>
        /// <param name="param10">要进行计算哈希值的对象</param>
        /// <param name="param11">要进行计算哈希值的对象</param>
        /// <param name="param12">要进行计算哈希值的对象</param>
        /// <param name="param13">要进行计算哈希值的对象</param>
        public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8, T9 param9, T10 param10, T11 param11, T12 param12, T13 param13)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                hash = hash * PrimeNumber + GetHashCode(param1);
                hash = hash * PrimeNumber + GetHashCode(param2);
                hash = hash * PrimeNumber + GetHashCode(param3);
                hash = hash * PrimeNumber + GetHashCode(param4);
                hash = hash * PrimeNumber + GetHashCode(param5);
                hash = hash * PrimeNumber + GetHashCode(param6);
                hash = hash * PrimeNumber + GetHashCode(param7);
                hash = hash * PrimeNumber + GetHashCode(param8);
                hash = hash * PrimeNumber + GetHashCode(param9);
                hash = hash * PrimeNumber + GetHashCode(param10);
                hash = hash * PrimeNumber + GetHashCode(param11);
                hash = hash * PrimeNumber + GetHashCode(param12);
                return hash * PrimeNumber + GetHashCode(param13);
            }
        }

        /// <summary>
        /// 使用给定的<paramref name="parameters"/>生成哈希值。
        /// </summary>
        /// <remarks>
        /// 参见 <see href="http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode"/>
        /// </remarks>
        /// <param name="parameters">要进行计算哈希值的对象数组</param>
        public static int GetHashCode<T>(params T[] parameters)
        {
            unchecked
            {
                var hash = DefaultHashValue;
                // ReSharper 禁用一次 LoopCanBeConvertedToQuery  [PERF]
                // ReSharper 禁用一次 ForCanBeConvertedToForeach [PERF]
                for (var i = 0; i < parameters.Length; i++)
                {
                    var param = parameters[i];
                    hash = hash * PrimeNumber + GetHashCode(param);
                }
                return hash;
            }
        }
    }
}
